﻿// Author: Iulian Lita
// Project: UDPServer
// Path: C:\Users\Tym\Documents\Visual Studio 2008\Projects\WebCrawlerProject\UDPServer
// Creation date: 1/11/2009 4:40 PM
// Last modified: 1/11/2009 5:58 PM

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Net.Sockets;
using Error;
using System.Net;
using Database;
using System.Threading;
using UDPServer.DataNamespace;
using UDPServer.CommandNamespace;
using System.Windows.Forms;

namespace UDPServer
{
    public class Server
    {
        private static int _seedId = 0;
        ListView clients_lview;

        //The ClientInfo structure holds the required information about every
        //client connected to the server
       public class ClientInfo
        {
            public EndPoint endpoint;   //Socket of the client
            public string strName;      //Name by which the user logged into the chat room
            public string lastSeed = null;
            public int numberOfRequest = 0;
            
            public ClientInfo()
            {
                numberOfRequest = 0;
            }
        }

        //The collection of all clients logged into the room (an array of type ClientInfo)
        public List<ClientInfo> clientList;

        //The main socket on which the server listens to the clients
        Socket serverSocket;

        byte[] byteData = new byte[1024];

        public Server( ref ListView _clients_lview )
        {
            clientList = new List<ClientInfo>();
            clients_lview = _clients_lview;
            try
            {
                //We are using UDP sockets
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

                //Assign the any IP of the machine and listen on port number 1000
                IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 1000);

                //Bind this address to the server
                serverSocket.Bind(ipEndPoint);

                IPEndPoint ipeSender = new IPEndPoint(IPAddress.Any, 0);
                //The epSender identifies the incoming clients
                EndPoint epSender = (EndPoint)ipeSender;

                //Start receiving data
                serverSocket.BeginReceiveFrom(byteData, 0, byteData.Length, SocketFlags.None, ref epSender, new AsyncCallback(OnReceive), epSender);
            }
            catch (Exception ex)
            {
                ErrorManagement.ManageException(ex);
            }
        }

        private void OnReceive(IAsyncResult ar)
        {
            try
            {
                IPEndPoint ipeSender = new IPEndPoint(IPAddress.Any, 0);
                EndPoint epSender = (EndPoint)ipeSender;

                serverSocket.EndReceiveFrom(ar, ref epSender);

                
                //Transform the array of bytes received from the user into an
                //intelligent form of object Data
                Data msgReceived = new Data(byteData);

                //We will send this object in response the users request
                Data msgToSend = new Data();

                byte[] message;

                //If the message is to login, logout, or simple text message
                //then when send to others the type of the message remains the same
                msgToSend.cmdCommand = msgReceived.cmdCommand;
                msgToSend.strName = msgReceived.strName;

                switch (msgReceived.cmdCommand)
                {
                    case Command.Login:

                        //When a user logs in to the server then we add her to our
                        //list of clients

                        ClientInfo clientInfo = new ClientInfo();
                        clientInfo.endpoint = epSender;
                        clientInfo.strName = msgReceived.strName;

                        clientList.Add(clientInfo);
                        clients_lview.Items.Add( (clientList.Count - 1).ToString() );
                        string[] subItems = { "", "", "" };
                        clients_lview.Items[clientList.Count-1].SubItems.AddRange( subItems );
                        clients_lview.Items[clientList.Count- 1].SubItems[1].Text = clientInfo.strName;
                        
                        //Set the text of the message that we will broadcast to all users
                        msgToSend.strMessage += "OK";
                        message = msgToSend.ToByte();
                        serverSocket.BeginSendTo(message, 0, message.Length, SocketFlags.None, epSender, new AsyncCallback(OnSend), epSender);
                        break;

                    case Command.Logout:

                        //When a user wants to log out of the server then we search for her 
                        //in the list of clients and close the corresponding connection

                        int nIndex = 0;
                        foreach (ClientInfo client in clientList)
                        {                            
                            if (client.endpoint == epSender)
                            {
                                clientList.RemoveAt(nIndex);
                                break;
                            }
                            ++nIndex;
                        }

                        break;

                    case Command.Seed:


                        msgToSend.cmdCommand = Command.Seed;
                        msgToSend.strName = null;
                        msgToSend.strMessage = null;
                        //Collect the names of the user in the chat room
                        

                        Monitor.Enter(this);
                        DatabaseWork dw = new DatabaseWork();
                        _seedId++;

                        
                        if (_seedId <= dw.CountSeed())
                        {
                            foreach (ClientInfo client in clientList)
                            {
                                IPEndPoint clientEndPoint = (IPEndPoint)client.endpoint;
                                IPEndPoint senderEndPoint = (IPEndPoint)epSender;

                                if (clientEndPoint.Address.Equals(senderEndPoint.Address))
                                {
                                    client.numberOfRequest++;
                                    client.lastSeed = dw.GetSeed(_seedId);
                                    clients_lview.Items[clientList.Count - 1].SubItems[2].Text = client.lastSeed;
                                    clients_lview.Items[clientList.Count - 1].SubItems[3].Text = client.numberOfRequest.ToString();
                                    msgToSend.strMessage = client.lastSeed;
                                    break;
                                }

                            }
                            
                        }
                        else
                        {
                            msgToSend.strMessage += "END";
                        }
                        Monitor.Exit(this);
                        
                        message = msgToSend.ToByte();

                        serverSocket.BeginSendTo(message, 0, message.Length, SocketFlags.None, epSender, new AsyncCallback(OnSend), epSender);
                        break;
                }

                //If the user is logging out then we need not listen from her
                if (msgReceived.cmdCommand != Command.Logout)
                {
                    //Start listening to the message send by the user
                    serverSocket.BeginReceiveFrom(byteData, 0, byteData.Length, SocketFlags.None, ref epSender, new AsyncCallback(OnReceive), epSender);
                }
            }
            catch (Exception ex)
            {
                ErrorManagement.ManageException(ex);
            }
        }

        public void OnSend(IAsyncResult ar)
        {
            try
            {
                serverSocket.EndSend(ar);
            }
            catch (Exception ex)
            {
                ErrorManagement.ManageException(ex);
            }
        }
    }
}
